package xyz.chaobei.mall.security.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import xyz.chaobei.mall.security.config.JwtConfig;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 默认JWT 过滤器
 *
 * @author <a href='mailto:maruichao52@gmail.com'>MRC</a>
 * @since 2020/10/15
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private DefaultTokenServer defaultTokenServer;

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * <p> token 过滤器逻辑
     * 1、token 必须存在
     * 2、toKen 必须正确，未过期。
     * 3、若上下文不存在。则往上下文放一个userDetail
     * <p>author: <a href='mailto:maruichao52@gmail.com'>MRC</a>
     *
     * @param request     请求
     * @param response    响应
     * @param filterChain 过滤器
     * @return void
     * @since 2020/10/22
     **/
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String token = request.getHeader(jwtConfig.getTokenHeader());

        // 请求携带token/则检验这个token是否正确和是否过期
        if (!StringUtils.isEmpty(token)) {

            log.info("doFilterInternal request url={}", request.getRequestURI());

            // 携带的用户名信息
            String username = defaultTokenServer.getUserNameFromToken(token);
            log.info("doFilterInternal token username={}", username);

            if (StringUtils.isEmpty(username)) {
                filterChain.doFilter(request, response);
            }
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            //校验token是否有效
            if (defaultTokenServer.isTokenExpired(token)) {
                filterChain.doFilter(request, response);
            }
            //检查当前上下文是否存在用户信息，若没有则添加
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                // 将用户信息添加到上下文。说明这个request 是通过的。
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                log.info("doFilterInternal user={}", username);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        // 通过拦截器
        filterChain.doFilter(request, response);
    }
}
